`timescale 1ns/1ps
`default_nettype none

/* NOTE:
*  - 串移数据
*/

module data_shift_mbi6334
    #(
    parameter   DW      = 1
    )
    (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [7:0]   I_cfg_clock_low,   // 时钟低电平时钟数
    input  wire [7:0]   I_cfg_clock_cycle, // 时钟整周期时钟数
    input  wire [7:0]   I_cfg_clock_phase, // 时钟相位
    // shift request
    input  wire         I_shift_req,       // 串移开始
    output wire         O_shift_busy,      // 正在串移
    input  wire [14:0]   I_shift_bit_num,   // 串移长度(bit)
    // input  wire [5:0]   I_shift_load_num,  // load宽度
    input  wire [DW-1:0] I_shift_data,      // 串移数据
    output wire         O_shift_data_ack,  // 数据确认
    // data out
    // output wire         O_load_out,
    output wire         O_clock_out,
    output wire [DW-1:0] O_data_out
);
//------------------------Parameter----------------------
localparam
    L0 = 2,  // 时钟输出延时时间
    L1 = 1;  // load输出延时时间

//------------------------Local signal-------------------
// clock output
reg  [7:0]  clk_cnt;
reg         clk_out;
reg  [L0:0] clk_sr;
reg         clk_en;

// data output
reg  [14:0]  data_cnt;
reg  [DW-1:0] data_buf0;
reg  [DW-1:0] data_buf1;
reg         data_ack;

// load output
reg         load_out;
reg  [5:0]  load_cnt;
reg  [7:0]  load_clk_cnt;
reg  [L1:0] load_sr;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++clock output+++++++++++++++++++
assign O_clock_out = clk_sr[L0-1];

// clk_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_cnt <= 1'b1;
    else if (~clk_en)
        clk_cnt <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle)
        clk_cnt <= 1'b1;
    else
        clk_cnt <= clk_cnt + 1'b1;
end

// clk_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_out <= 1'b0;
    else if (~clk_en)
        clk_out <= 1'b0;
    else if (clk_cnt == I_cfg_clock_low)
        clk_out <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle)
        clk_out <= 1'b0;
end

// clk_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_sr <= 1'b0;
    else
        clk_sr <= {clk_sr[L0-1:0], clk_out};
end

// clk_en
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        clk_en <= 1'b0;
    else if (I_shift_req)
        clk_en <= 1'b1;
    else if (clk_cnt == I_cfg_clock_cycle && data_cnt == 1'b1)
        clk_en <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++data output++++++++++++++++++++
assign O_data_out = data_buf1;

assign O_shift_busy = clk_en;
assign O_shift_data_ack = data_ack;

// data_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_cnt <= 1'b0;
    else if (I_shift_req)
        data_cnt <= I_shift_bit_num;
    else if (clk_cnt == I_cfg_clock_cycle)
        data_cnt <= data_cnt - 1'b1;
end

// data_buf0
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf0 <= 1'b0;
    else if (data_ack)
        data_buf0 <= I_shift_data;
end

// data_buf1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_buf1 <= 1'b0;
    else
        data_buf1 <= data_buf0;
end

// data_ack
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        data_ack <= 1'b0;
    else if (clk_en && clk_cnt == I_cfg_clock_phase)
        data_ack <= 1'b1;
    else
        data_ack <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
